package com.qing.common.util

import android.content.Context
import android.content.res.AssetFileDescriptor
import android.media.MediaCodec
import android.media.MediaExtractor
import android.media.MediaFormat
import android.net.Uri
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.math.abs

object AudioWaveformUtils {

    private const val TAG = "AudioWaveformUtils"

    /**
     * 读取 WAV 文件的 PCM 数据
     */
    fun readWavPcmData(file: File): ShortArray {
        val inputStream = FileInputStream(file)
        val header = ByteArray(44) // WAV 文件头固定 44 字节
        inputStream.read(header)

        // 从文件头解析采样率、声道数、位深等信息
        val dataSize =
            header[40].toInt() and 0xFF or (header[41].toInt() and 0xFF shl 8) or (header[42].toInt() and 0xFF shl 16) or (header[43].toInt() and 0xFF shl 24)

        // 读取 PCM 数据（假设为 16 位深）
        val pcmData = ByteArray(dataSize)
        inputStream.read(pcmData)
        inputStream.close()

        // 将 ByteArray 转换为 ShortArray（16-bit 小端序）
        return pcmData.toShortArray()
    }

    fun decodeMp32Pcm(uri: String): ShortArray {
        val extractor = MediaExtractor()
        extractor.setDataSource(uri)
        return decodeMp3ToPcm(extractor)
    }

    fun decodeMp32Pcm(context: Context, mp3Path: String): ShortArray {

        val wavPath = FileUtils.getCacheWavPath(context)
        val result = mp32Wav(mp3Path, wavPath)
        LogUtil.d(TAG, "decodeMp32Pcm: $result")

        val extractor = MediaExtractor()
        extractor.setDataSource(wavPath)
        try {
            return decodeMp3ToPcm(extractor)
        } finally {
            FileUtils.deleteFile(wavPath)
        }
    }

    fun decodeMp3ToPcm(assetFileDescriptor: AssetFileDescriptor): ShortArray {
        val extractor = MediaExtractor()
        extractor.setDataSource(assetFileDescriptor)
        return decodeMp3ToPcm(extractor)
    }


    /**
     * 将 MP3 文件解码为 PCM 数据
     */
    private fun decodeMp3ToPcm(extractor: MediaExtractor): ShortArray {
        val trackIndex = selectAudioTrack(extractor)
        extractor.selectTrack(trackIndex)

        val format = extractor.getTrackFormat(trackIndex)
        val mime = format.getString(MediaFormat.KEY_MIME)!!
        val codec = MediaCodec.createDecoderByType(mime)
        codec.configure(format, null, null, 0)
        codec.start()

        val bufferInfo = MediaCodec.BufferInfo()
        val outputBuffers = mutableListOf<ByteArray>()
        var inputEof = false
        var outputEof = false

        while (!outputEof) {
            if (!inputEof) {
                val inputBufferId = codec.dequeueInputBuffer(10000)
                if (inputBufferId >= 0) {
                    val inputBuffer = codec.getInputBuffer(inputBufferId)!!
                    val sampleSize = extractor.readSampleData(inputBuffer, 0)
                    if (sampleSize < 0) {
                        codec.queueInputBuffer(
                            inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
                        )
                        inputEof = true
                    } else {
                        codec.queueInputBuffer(
                            inputBufferId, 0, sampleSize, extractor.sampleTime, 0
                        )
                        extractor.advance()
                    }
                }
            }

            when (val outputBufferId = codec.dequeueOutputBuffer(bufferInfo, 10000)) {
                MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
                    // 处理格式变化
                }

                MediaCodec.INFO_TRY_AGAIN_LATER -> {
                    // 重试
                }

                else -> {
                    if (outputBufferId >= 0) {
                        val outputBuffer = codec.getOutputBuffer(outputBufferId)!!
                        val chunk = ByteArray(bufferInfo.size)
                        outputBuffer.get(chunk)
                        outputBuffers.add(chunk)
                        codec.releaseOutputBuffer(outputBufferId, false)
                        if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) {
                            outputEof = true
                        }
                    }
                }
            }
        }

        codec.stop()
        codec.release()
        extractor.release()

        // 合并所有解码后的 PCM 数据并转换为 ShortArray
        val totalBytes = outputBuffers.sumOf { it.size }
        val merged = ByteArray(totalBytes)
        var offset = 0
        outputBuffers.forEach {
            System.arraycopy(it, 0, merged, offset, it.size)
            offset += it.size
        }
        return merged.toShortArray()
    }

    private fun selectAudioTrack(extractor: MediaExtractor): Int {
        for (i in 0 until extractor.trackCount) {
            val format = extractor.getTrackFormat(i)
            val mime = format.getString(MediaFormat.KEY_MIME)
            if (mime?.startsWith("audio/") == true) {
                return i
            }
        }
        throw IllegalArgumentException("No audio track found")
    }

    fun generateWaveform(pcmData: ShortArray, samplesPerPoint: Int = 100): FloatArray {
        val totalSamples = pcmData.size
        val numPoints = (totalSamples + samplesPerPoint - 1) / samplesPerPoint // 向上取整
        val waveform = FloatArray(numPoints)

        for (i in 0 until numPoints) {
            val start = i * samplesPerPoint
            val end = minOf(start + samplesPerPoint, totalSamples)
            var sum = 0f
            for (j in start until end) {
                sum += abs(pcmData[j].toFloat() / Short.MAX_VALUE) // 归一化到 [-1, 1]
            }
            waveform[i] = sum / (end - start) // 平均幅度
        }
        return waveform
    }

    fun generatePeakWaveform(pcmData: ShortArray, samplesPerPoint: Int = 100): FloatArray {
        val totalSamples = pcmData.size
        val numPoints = (totalSamples + samplesPerPoint - 1) / samplesPerPoint
        val waveform = FloatArray(numPoints)

        for (i in 0 until numPoints) {
            val start = i * samplesPerPoint
            val end = minOf(start + samplesPerPoint, totalSamples)
            var max = 0f
            for (j in start until end) {
                val value = abs(pcmData[j].toFloat() / Short.MAX_VALUE)
                if (value > max) max = value
            }
            waveform[i] = max
        }
        return waveform
    }
}

fun ByteArray.toShortArray(): ShortArray {
    val shortArray = ShortArray(size / 2)
    ByteBuffer.wrap(this).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortArray)
    return shortArray
}